using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
using Microsoft.Web.Mvc.Controls;
using Universum.Views.Resource;
using Universum.Infrastructure.Persistence;
using System.Security.Principal;
using Universum.Model.Domain;
using Universum.Model.Services;
using System.IO;
using Universum.Infrastructure.Persistence.Queries;
using Universum.Infrastructure.Configuration;
using System.Text;
using Universum.Infrastructure.Utils;
using System.Reflection;

namespace Universum.Controllers
{
	[Authorize(Roles = ConstantIdentifiers.ROLE_OF_TEACHER_OR_STUDENT+", "+ConstantIdentifiers.ROLE_OF_SUPER)]
	public class ResourceController : UniversumController
	{
		IFileService FileService { get; set; }
		IFriendlyUrlService SlugService { get; set; }
		IRepository Repository { get; set; }
		public ResourceController(

			IRepository repository,
			IPrincipal user,
			IFileService fileService,
			IFriendlyUrlService slugService,
			IPathConfigurationService configurationService
			)
			: base(user)
		{
			Repository = repository;
			FileService = fileService;
			SlugService = slugService;

		}

		
		public ActionResult Browse(string path)
		{
			var resourceViewModel = new
		ResourceViewModel();

			provide_model_with_default_information(path, resourceViewModel);
			resourceViewModel.Directories = FileService.GetAllDirectoriesFor(path);
			resourceViewModel.FileTypes = new Dictionary<string, IList<FileTypeViewModel>>();
			var files = FileService.GetAllFilesFor(path);
			//var allResources = Repository.Query<Resource>().ToList();
			var resourcesMatched = Repository.Query(
				new ResourcesByPaths(files)).ToList();
			foreach (var matched_resource in resourcesMatched)
			{
				if (ContentTypeRepresentationService.IsImageContent(matched_resource.ContentType).IsFalse()) continue;
				if (resourceViewModel.FileTypes.Keys.Contains(matched_resource.ContentType).IsFalse())
				{
					resourceViewModel.FileTypes.Add(matched_resource.ContentType,
						new List<FileTypeViewModel>());
				}

				resourceViewModel.FileTypes[matched_resource.ContentType]
					.Add(new FileTypeViewModel
					{
						FileName = matched_resource.FileName,
						Id = matched_resource.ID.ToString(),
						IsImage = matched_resource.ContentType.Contains("image"),
						IsContentPresentationPossible =
					false

					});
			}
			ViewData.Model = resourceViewModel;

			return View();
		}
		//
		// GET: username/Resource/

		public ActionResult Index(string path)
		{

			var resourceViewModel = new
		ResourceViewModel();

			provide_model_with_default_information(path, resourceViewModel);
			resourceViewModel.Directories = FileService.GetAllDirectoriesFor(path);
			resourceViewModel.FileTypes = new Dictionary<string, IList<FileTypeViewModel>>();
			var files = FileService.GetAllFilesFor(path);
			var allResources = Repository.Query<Resource>().ToList();
			var resourcesMatched = Repository.Query(
				new ResourcesByPaths(files)).ToList();
			foreach (var matched_resource in resourcesMatched)
			{
				if (resourceViewModel.FileTypes.Keys.Contains(matched_resource.ContentType).IsFalse())
				{
					resourceViewModel.FileTypes.Add(matched_resource.ContentType,
						new List<FileTypeViewModel>());
				}

				resourceViewModel.FileTypes[matched_resource.ContentType]
					.Add(new FileTypeViewModel
					{
						FileName = matched_resource.FileName,
						Id = matched_resource.ID.ToString(),
						IsImage = matched_resource.ContentType.Contains("image"),
						IsContentPresentationPossible =
						ContentTypeRepresentationService
							.IsPresentableContent(matched_resource
							.ContentType)

					});
			}
			ViewData.Model = resourceViewModel;

			return View();
		}

		private void provide_model_with_default_information(string path, ResourceViewModel resourceViewModel)
		{
			resourceViewModel.IsInsideFolder = path.IsNullOrEmpty();
			resourceViewModel.UserPath = path;
			resourceViewModel.Username = CurrentUsername;
		}

		//
		// GET: /Resource/CreateFile

		public ActionResult CreateFile(string path)
		{
			var resourceViewModel = new ResourceViewModel();
			provide_model_with_default_information(path, resourceViewModel);
			ViewData.Model = resourceViewModel;
			return View();
		}

		//
		// POST: /Resource/CreateFile/toFolder

		[AcceptVerbs(HttpVerbs.Post)]
		public ActionResult CreateFile(HttpPostedFileBase resourceFile, string toFolder)
		{
			Func<string, bool> isAcceptedArhive = it =>
				it.EndsWith(".zip")
				|| it.EndsWith(".rar") || it.EndsWith(".7zip");

			if (resourceFile.IsNull())
			{
				ModelState.AddModelError(MemberUtil<HttpPostedFileBase>.MemberName(m => m.InputStream), "You have to specify a resource");
				provide_model_on_error(toFolder);
			}
			else

				if (ConstantIdentifiers.accepted_file_types.Contains(resourceFile.ContentType).IsFalse()
					&& isAcceptedArhive(resourceFile.FileName).IsFalse()
					)
				{
					ModelState.AddModelError("ContentType", "The file you were trying to upload is not of an accepted type");
					provide_model_on_error(toFolder);
					return View("CreateFile");
				}
			try
			{
				Func<string, string> determine_content_type_from_extension =
					it =>
					{
						if (it.EndsWith(".zip")) return
						   ConstantIdentifiers.FILETYPE_APPLICATION_ZIP;
						if (it.EndsWith(".rar"))
							return
						ConstantIdentifiers.FILETYPE_APPLICATION_RAR;
						if (it.EndsWith(".7zip"))
							return ConstantIdentifiers.FILETYPE_APPLICATION_7ZIP;
						return null;

					};

				Resource resource = new Resource();
				resource.ContentType = resourceFile.ContentType ?? determine_content_type_from_extension(resourceFile.FileName);
				resource.ID = Guid.NewGuid();
				resource.Owner = User.Identity.Name;
				resource.Path =
					System.IO.Path.Combine(CurrentUsername,
					System.IO.Path.Combine(toFolder, resourceFile.FileName));
				resource.FileName = resourceFile.FileName;
				resource.ContentLength = resourceFile.ContentLength;

				if (resource.ContentType.IsNullOrEmpty())
					throw new ApplicationException("No content type found");
				Repository.SaveOrUpdate(resource);

				FileService.WriteToDiskFrom(resourceFile, resource.Path);
				return RedirectToAction<ResourceController>(view => view.Index(toFolder));
			}
			catch (Exception ex)
			{
				ModelState.AddModelError("ContentType", ex.Message);
				provide_model_on_error(toFolder);
				return View("CreateFile");
			}
		}

		private void provide_model_on_error(string toFolder)
		{
			var resourceViewModel = new ResourceViewModel();
			provide_model_with_default_information(toFolder, resourceViewModel);
			resourceViewModel.IsAcceptedContentType = false;
			ViewData.Model = resourceViewModel;
		}
		//
		// Post: Resource/Download/resourceId
		[AcceptVerbs(HttpVerbs.Post)]
		public ActionResult DownloadFile(string resourceId)
		{
			var guid = new Guid(resourceId);
			var resource =
				Repository.Load<Resource>(
				guid);

			var stream = FileService.GetStreamFor(resource.Path);
			return
				new FileStreamResult
				(stream, resource.ContentType)
				{
					FileDownloadName = resource.FileName
				};

		}
		public ActionResult Details(string id)
		{
			var guid = new Guid(id);
			var resource =
				Repository.Load<Resource>(
				guid);
			ViewData.Model = resource;
			return View();
		}
		//
		// GET: /Resource/CreateFolder

		public ActionResult CreateFolder()
		{
			var resourceViewModel = new ResourceViewModel();
			provide_model_with_default_information("", resourceViewModel);
			
			return View();
		}

		//
		// POST: /Resource/CreateFolder

		[AcceptVerbs(HttpVerbs.Post)]
		public ActionResult CreateFolder(string newFolderName,
			string path)
		{
			try
			{
				// in order to escape unwanted characters
				newFolderName = SlugService.CreateSlugFrom(newFolderName);
				FileService.WriteDirectory(newFolderName,
					path);
				return RedirectToAction<ResourceController>(
					invokeAction => invokeAction.Index(path));
			}
			catch (Exception)
			{
				return View();
			}
		}

		// 
		// POST: /Resource/DeleteFolder/folder-name
		[AcceptVerbs(HttpVerbs.Post)]
		public ActionResult DeleteFolder(string path)
		{

			FileService.DeleteFolder(path);
			return RedirectToAction<ResourceController>(
				to => to.Index(""));

		}
		// 
		// POST: /Resource/DeleteFile/folder-path/file-id
		[AcceptVerbs(HttpVerbs.Post)]
		public ActionResult DeleteFile(string folderPath, string id)
		{
			var guid = new Guid(id);
			var file = Repository.Load<Resource>(guid);
			FileService.DeleteFile(file.Path);
			Repository.Remove(file);
			return RedirectToAction<ResourceController>(
				to => to.Index(folderPath));

		}

		[AcceptVerbs(HttpVerbs.Get)]
		public ActionResult ShowFile(string resourceId)
		{
			var guid = new Guid(resourceId);
			var resource =
				Repository.Load<Resource>(
				guid);

			string ContentType = "";
			Stream stream;
			if (resource != null)
			{
				stream = FileService.GetStreamFor(resource.Path);
				ContentType = resource.ContentType;
			}
			else
			{

				stream = typeof(ResourceController).Assembly.GetManifestResourceStream("Universum.Content.images.not-found.png");
				ContentType = "image/png";


			}
			byte[] fileContents = new byte[] { 0 };
			using (var binaryReader = new BinaryReader(stream))
			{
				fileContents = binaryReader.ReadBytes((int)stream.Length);
			}

			return new FileContentResult(
				fileContents,
				ContentType);

		}

	}
}
